前端三大框架小弟我除了Angular之外都算熟,之後的文章可能會帶入點React和Vue的部分,畢竟現在網頁工程師或多或少都碰過三大框架之一。
custom element 主要由二部分組成,第一部分是利用JS建立一個可以生成custom element的constructor,另一部分是使用web API把constructor註冊到window。
constructor通常都是使用Class來extend HTMLElement的方式來建構,這部分就像是react 的 class component。
constructor可以使用Prototype來建構,但討厭class語法如小弟我,也不建議用class以外的方式來建構。
/* 簡單寫法 */
class MyElement extends HTMLElement { // 必要
constructor() { // constructor非必要,但建議加入
super();
}
connectedCallback() { // 必要,大部分程式碼都請寫在這裡
// 通常是建議使用shadow DOM,而不是直接寫入this,等到介紹Shadow時再說明
this.innerHTML= `<div>Hallo World!</div>`
}
}
/* 參考react class conponent後的,我常用的寫法 */
class MyElement extends HTMLElement {
constructor() {
super();
this.render = this.render.bind(this);
}
connectedCallback() {
this.innerHTML= this.render()
}
render() {
return <div>Hallo World!</div>
}
}
/* react class component的對應寫法 */
class MyElement extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div>Hallo World!</div>
}
}
Web API的部分就簡單多了,使用customElements這個全域物件內的define方法就能把前一部分的constructor註冊到window上了。但有一個限制,註冊的名稱中至少要有一個'-'
customElements.define('my-element', MyElement);
// 正確的用法
customElements.define('my-element-first', MyElement);
// 正確的用法,至少有使用到一個"-"
customElements.define('myelement', MyElement);
// 錯誤的用法,沒有使用到'-'
HTML中使用就簡單多了,直接當成普通的HTML元素來使用就可以了
<section>
<!-- 正常的用法 -->
<my-element></my-element>
</section>
<section>
<!-- 正常的用法,但也許有些瀏覽器會報錯 -->
<my-element />
</section>
<section>
<!-- 不管是<my-element></my-element>或 <my-element /> ,都會做出下面的樣子-->
<div>Hallo World!</div>
</section>
帶我進入軟體業的前輩曾經這樣說,一個Todo-list的網頁是最適合用來練習前後端的系統了。本系列的本章都會以todolist當例子
<!-- 對照組 -->
<section id="target" class="todolist">
<article class="todo-item">
<input type="checkbox" name="check" />
<h1 class="item-title">今天晚上公司聚餐</h1>
<div class="item-tags">
<span class="item-tag">吃飯</span>
<span class="item-tag">公司</span>
</div>
<p class="item-descption">吃到飽餐廳吃飯</p>
</article>
</section>
<!-- 傳統的Javascript用法 -->
<section id="tradition" class="todolist"></section>
<!-- 使用web component -->
<section id="web-component" class="todolist">
<todo-item></todo-item>
</section>
/* 傳統的Javascript用法 */
function todoItemJS ({parentNode}) {
const rootArticle = document.createElement('article');
rootArticle.classList.add('todo-item');
rootArticle.innerHTML= `
<input type="checkbox" name="check" />
<h1 class='item-title'>今天晚上公司聚餐</h1>
<div class='item-tags'>
<span class=='item-tag'>吃飯</span>
<span class=='item-tag'>公司</span>
</div>
<p class='item-descption'>吃到飽餐廳吃飯</p>
`
parentNode.appendChild(rootArticle);
}
const parentNode = document.getElementById('tradition');
todoItemJS({parentNode})
/* web component */
class TodoItem extends HTMLElement {
constructor() {
super();
this.render = this.render.bind(this);
}
connectedCallback() {
this.innerHTML= this.render()
}
render() {
// render the element
const rootArticle = document.createElement('article');
rootArticle.classList.add('todo-item');
rootArticle.innerHTML= `
<input type="checkbox" name="check" />
<h1 class='item-title'>今天晚上公司聚餐</h1>
<div class='item-tags'>
<span class=='item-tag'>吃飯</span>
<span class=='item-tag'>公司</span>
</div>
<p class='item-descption'>吃到飽餐廳吃飯</p>
`
return rootArticle;
}
}
customElements.define('todo-item', TodoItem);